package asteroids.model;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

import asteroids.CollisionListener;
import asteroids.ModelException;
import asteroids.Util;
import asteroids.util.Vector;
import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Immutable;

/**
 * @param height
 *            Height of the world
 * @param width
 *            Width of the world
 * @param MAX_HEIGHT
 *            The maximum height a world can be
 * @param MAX_WIDTH
 *            The maximum width a world can be
 * @param ships
 *            HashSet containing all the ships in this world
 * @param asteroids
 *            HashSet containing all asteroids in this world
 * @param bullets
 *            HashSet containing all bullets in this world
 * @param spaceObjects
 *            HashSet containing all objects of this world (this is redundant
 *            data, but it makes the proceedings of the class easier)
 * @post ...
 *       | new.getWidth() == width
 * @post ...
 *       | new.getHeight() == height
 * 
 * @invar All objects must be within the boundaries of the world
 *        | for each SpaceObject o in spaceObjects:
 *        | SpaceObject.isValidPosition(o.getPosition) == true
 * 
 * @invar Objects never overlap, except for bullets and their source
 *        | for each SpaceObject o in spaceObjects:
 *        if(o instanceof Bullet && other instanceof Ship && o.getSource() == other)
 *        Is allowed to be true.
 * 
 *        else
 *        overlaps(spaceObject) == false;
 * 
 * 
 * @author Michiel De Cuyper & Lennart De Graef
 * 
 */
public class World {

	/**
	 * 
	 * @param width
	 *            The width of the world
	 * @param height
	 *            The height of the world
	 */
	public World(double width, double height) {
		setHeight(height);
		setWidth(width);
	}

	/**
	 * 
	 * @return this.height
	 */
	@Basic
	@Immutable
	public double getHeight() {
		return height;
	}

	/**
	 * Sets the height of the world to the given parameter, implemented TOTALLY.
	 * When an invalid height is given, the value defaults to 100
	 * 
	 * @param height
	 *            the height of the world to be set.
	 * @post ...
	 *       |if (isValidHeight) (new this).height = height
	 *       else (new this).height = 1000
	 */
	private void setHeight(double height) {
		if (isValidHeight(height)) this.height = height;
		else throw new ModelException("Invalid height");
	}

	/**
	 * 
	 * @param height
	 *            height to be checked
	 * @return ...
	 *         | true if(this.height != Double.NaN && this.height <= MAX_HEIGHT
	 *         && this.height >= 0)
	 */
	private static boolean isValidHeight(double height) {
		if (height == Double.NaN) return false;
		else if (height >= MAX_HEIGHT) return false;
		else if (height <= 0) return false;
		else return true;
	}

	/**
	 * 
	 * @return ...
	 *         |return MAX_HEIGHT
	 */
	@Basic
	@Immutable
	public static double getMaxHeight() {
		return MAX_HEIGHT;
	}

	private double height;
	private static final double MAX_HEIGHT = Double.MAX_VALUE;

	/**
	 * 
	 * @return this.width
	 * 
	 */
	@Basic
	@Immutable
	public double getWidth() {
		return width;
	}

	/**
	 * Sets the width of the world to the given parameter, implemented TOTALLY.
	 * When an invalid width is given, the value defaults to 1000
	 * 
	 * @param width
	 *            the width of the world
	 * @post ...
	 *       |if (isValidWidth) (new this).width = width
	 *       else (new this).width = 1000
	 */
	private void setWidth(double width) {
		if (isValidWidth(width)) this.width = width;
		else throw new ModelException("Invalid width");

	}

	/**
	 * @param width
	 *            width to be checked
	 * @return ...
	 *         |true if(this.width != Double.NaN && this.width <= MAX_HEIGHT &&
	 *         this.width >= 0)
	 */
	private static boolean isValidWidth(double width) {
		if (width == Double.NaN) return false;
		else if (width >= MAX_WIDTH) return false;
		else if (width <= 0) return false;
		else return true;
	}

	/**
	 * 
	 * @return MAX_WIDTH
	 */
	@Basic
	@Immutable
	public static double getMaxWidth() {
		return MAX_WIDTH;
	}

	private static final double MAX_WIDTH = Double.MAX_VALUE;
	private double width;

	/**
	 * 
	 * @return this.ships
	 */
	@Basic
	public Set<Ship> getShips() {
		return ships;
	}

	/**
	 * Adds a ship to the world.
	 * 
	 * @param ship
	 * 
	 * @post ...
	 *       |if(isWithinBounds(ship) && !overlaps(ship))
	 *       ships.contains(ship) == true
	 *       spaceObjects.contains(ship) == true
	 */
	public void addShip(Ship ship) {
		try {
			overlaps(ship);
		}
		catch (ModelException e) {
			//end method, no ship is placed.
			return;
		}
		ships.add(ship);
		spaceObjects.add(ship); // TODO This causes redundant data, okay for
								// first implementation, needs optimization.
	}

	/**
	 * 
	 * @param ship
	 * 
	 * @post ...
	 *       |if(ship =! null)
	 *       ships.contains(ship) == false
	 *       spaceObjects.contains(ship) == false
	 */
	public void removeShip(Ship ship) {
		try {
			ships.remove(ship);
			spaceObjects.remove(ship);
		}
		catch (NullPointerException e) {
			return;
		}

	}

	private Set<Ship> ships = new HashSet<Ship>();

	/**
	 * 
	 * @return this.asteroids
	 */
	@Basic
	public Set<Asteroid> getAsteroids() {
		return asteroids;
	}

	/**
	 * Adds an asteroid to the world.
	 * 
	 * @param asteroid
	 * 
	 * @post ...
	 *       |if(isWithinBounds(asteroid) && !overlaps(asteroid))
	 *       asteroids.contains(asteroid) == true
	 *       spaceObjects.contains(asteroid) == true
	 */
	public void addAsteroid(Asteroid asteroid) {
		try {
			overlaps(asteroid);
		}
		catch (ModelException e) {
			//end method, no asteroid is placed.
			return;
		}
		asteroids.add(asteroid);
		spaceObjects.add(asteroid);
	}

	/**
	 * 
	 * @param asteroid
	 * 
	 * @post ...
	 *       |if(asteroid =! null)
	 *       asteroids.contains(asteroid) == false
	 *       spaceObjects.contains(asteroid) == false
	 */
	public void removeAsteroid(Asteroid asteroid) {
		try {
			asteroids.remove(asteroid);
			spaceObjects.remove(asteroid);
		}
		catch (NullPointerException e) {
			return;
		}
	}

	private Set<Asteroid> asteroids = new HashSet<Asteroid>();

	/**
	 * 
	 * @return this.bullets
	 */
	@Basic
	public Set<Bullet> getBullets() {
		return bullets;
	}

	/**
	 * Adds bullet to the world. If the bullet overlaps with it's source, it is
	 * not deleted
	 * 
	 * @param bullet
	 * 
	 * @effect ...
	 *         | if(isWithinBounds(bullet) && !overlaps(bullet)
	 *         bullets.add(bullet)
	 *         spaceObjects.add(bullet)
	 *         else if(isWithinBounds(bullet) && overlaps(bullet)
	 *         resolveObjectCollision(bullet, other);
	 * 
	 */
	public void addBullet(Bullet bullet) {

		//If the source ship already fired 3 bullets, firing has no effect.

		if (bullet.getSource().getFiredBullets().size() >= 3) { return; }

		//If the new bullet overlaps with another object, they immediately collide.
		//The overlaps method is not used, since it is also necessary to know what entity it collides with.
		Iterator<SpaceObject> checkIt = spaceObjects.iterator();
		while (checkIt.hasNext()) {
			SpaceObject checkedEntity = checkIt.next();
			if (bullet.checkOverlap(checkedEntity)) {
				resolveObjectCollision(bullet, checkedEntity);
				//end method early.
				return;
			}
		}

		bullet.getSource().addFiredBullet(bullet);
		bullets.add(bullet);
		spaceObjects.add(bullet);
	}

	/**
	 * 
	 * @param bullet
	 * 
	 *            * @post ...
	 *            |if(bullet =! null)
	 *            bullets.contains(bullet) == false
	 *            spaceObjects.contains(bullet) == false
	 */
	public void removeBullet(Bullet bullet) {

		try {
			bullet.getSource().removeFiredBullet(bullet);
			bullets.remove(bullet);
			spaceObjects.remove(bullet);
		}
		catch (NullPointerException e) {
			return;
		}
	}

	private Set<Bullet> bullets = new HashSet<Bullet>();

	/**
	 * Checks whether the object overlaps with any other.
	 * 
	 * @param entity
	 * 
	 * @return ...
	 *         |result == for each SpaceObject o in spaceObjects:
	 *         !entity.checkOverlap(o)
	 **/
	private void overlaps(SpaceObject entity) throws ModelException {
		for (SpaceObject check : spaceObjects) {
			if (entity.checkOverlap(check)) throw new ModelException("Entity overlaps with another");
		}
	}

	/**
	 * 
	 */
	public Set<SpaceObject> getAll() {
		return spaceObjects;
	}

	// Set of all objects in the world, useful for evolve method.
	private Set<SpaceObject> spaceObjects = new HashSet<SpaceObject>();

	/**
	 * Advances all the objects in the world dt seconds.
	 * 
	 * @param dt
	 * 
	 * @effect ...
	 *         |
	 */
	public void evolve(double dt, CollisionListener collisionListener) {
		// 1: Prediction of the next collision (time to collision is tc).
		// First implementation, so it works. Will add optimization later.

		double tc = Double.POSITIVE_INFINITY;
		SpaceObject firstCollidingEntity1 = null;
		SpaceObject firstCollidingEntity2 = null;

		for (SpaceObject boundEntity : spaceObjects) {
			double timeToBoundCol = boundEntity.getTimeToBoundaryCollision();
			if (Util.fuzzyLessThanOrEqualTo(timeToBoundCol, tc)) {
				firstCollidingEntity1 = boundEntity;
				firstCollidingEntity2 = null;
				tc = timeToBoundCol;
			}
		}

		for (SpaceObject currentEntity : spaceObjects) {
			for (SpaceObject comparedEntity : spaceObjects) {
				double timeToObjectCol = currentEntity.getTimeToCollision(comparedEntity);
				if (Util.fuzzyLessThanOrEqualTo(timeToObjectCol, tc)) {
					firstCollidingEntity1 = currentEntity;
					firstCollidingEntity2 = comparedEntity;
					tc = timeToObjectCol;
				}
			}
		}

		// 2: If next collision happens sooner than dt, advance all objects for
		// tc seconds.
		// Otherwise, advance all objects dt seconds and end method.

		if (Util.fuzzyLessThanOrEqualTo(tc, dt)) { // Advance all objects for tc
													// seconds.
			for (SpaceObject entity : spaceObjects) {
				advance(entity, tc);
			}
			runAIProgram(tc);

		}
		else if (Util.fuzzyLessThanOrEqualTo(dt, tc)) {// Advance all objects
														// dt seconds, end
														// method.
			for (SpaceObject entity : spaceObjects) {
				advance(entity, dt);
			}
			runAIProgram(dt);
			return; // End method early.
		}
		// 3: Resolve the collision.
		collisionResolver(firstCollidingEntity1, firstCollidingEntity2, collisionListener);

		// 4: Advance all (dt-tc) seconds and go to step 1. This is accomplished by calling the evolve method again.

		for (SpaceObject entity : spaceObjects) {
			advance(entity, (dt - tc));
		}

		evolve(dt - tc, collisionListener);
	}

	/**
	 * Runs the AI programs on the ships according to the amount of time
	 * passed since last run
	 * 
	 * @param dt
	 */
	private void runAIProgram(double dt) {
		//Calculate the amount of times a program will run
		timeSinceLastRun += dt;
		int amountOfRuns = (int) Math.floor(timeSinceLastRun / 0.2);

		if (amountOfRuns >= 1) {
			for (Ship ship : ships) {
				if (ship.isAI()) ship.runProgram(amountOfRuns);
			}
			//Sets the value of timeSinceLastRun not to zero, but to mod 0.2
			timeSinceLastRun %= 0.2;
		}

	}

	//Time since the last execution of the AI programs
	private double timeSinceLastRun;

	/**
	 * Updates the position and velocity of an entity based on the parameter dt
	 * 
	 * @param entity
	 * @param dt
	 * 
	 * @effect ...
	 *         |entity.setPositionX(entity.getPositionX() +
	 *         entity.getVelocityX() * dt)
	 *         entity.setPositionY(entity.getPositionY() + entity.getVelocityY()
	 *         * dt)
	 *         setVelocityX(entity.getVelocityX() + entity.getAccelerationX() *
	 *         dt)
	 *         setVelocityY(entity.getVelocityY() + entity.getAccelerationY() *
	 *         dt)
	 */
	private void advance(SpaceObject entity, double dt) {
		entity.move(dt);

		double velX = entity.getVelocityX() + entity.getAccelerationX() * dt;
		double velY = entity.getVelocityY() + entity.getAccelerationY() * dt;

		entity.setVelocityX(velX);
		entity.setVelocityY(velY);
	}

	/**
	 * 
	 * @param firstCollidingEntity1
	 * @param firstCollidingEntity2
	 * @param collisionListener
	 * 
	 * @effect ...
	 *         if (firstCollidingEntity1 != null && firstCollidingEntity2 ==
	 *         null)
	 *         resolveBoundaryCollision(firstCollidingEntity1,collisionPosition[
	 *         0], collisionPosition[1]);
	 *         else if (firstCollidingEntity1 != null && firstCollidingEntity2
	 *         != null)
	 *         resolveObjectCollision(firstCollidingEntity1,
	 *         firstCollidingEntity2);
	 */
	private void collisionResolver(SpaceObject firstCollidingEntity1, SpaceObject firstCollidingEntity2, CollisionListener collisionListener) {

		if (firstCollidingEntity1 != null && firstCollidingEntity2 == null) {
			double[] collisionPosition = firstCollidingEntity1.getBoundaryCollisionPosition();
			collisionListener.boundaryCollision(firstCollidingEntity1, collisionPosition[0], collisionPosition[1]);
			resolveBoundaryCollision(firstCollidingEntity1, collisionPosition[0], collisionPosition[1]);
		}

		else if (firstCollidingEntity1 != null && firstCollidingEntity2 != null) {
			double[] collisionPosition = firstCollidingEntity1.getCollisionPosition(firstCollidingEntity2);
			collisionListener.objectCollision(firstCollidingEntity1, firstCollidingEntity2, collisionPosition[0], collisionPosition[1]);
			resolveObjectCollision(firstCollidingEntity1, firstCollidingEntity2);
		}
	}

	/**
	 * Resolves the collision between an entity an the boundary.
	 * 
	 * @param entity
	 * @param x
	 * @param y
	 * 
	 * @effect ...
	 *         if (entity instanceof Bullet && entity.hasBounced())
	 *         die(entity)
	 *         else
	 *         entity.setBounced()
	 *         if(Util.fuzzyEquals(posX, 0) || Util.fuzzyEquals(posX, width))
	 *         entity.setVelocityX(-entity.getVelocityX)
	 *         else if (Util.fuzzyEquals(posY, 0) || Util.fuzzyEquals(posY,
	 *         height))
	 *         entity.setVelocityY(-entity.getVelocityY())
	 */
	private void resolveBoundaryCollision(SpaceObject entity, double posX, double posY) {
		if (entity instanceof Bullet) { // Checks if the entity is a bullet and
										// has bounced. If true, kills bullet
										// and ends method.
			if (((Bullet) entity).hasBounced()) {
				die(entity);
				return;
			}
			else ((Bullet) entity).setBounced();
		}
		// 4 Cases for the changing of direction, can condense into 2
		// statements.
		if (Util.fuzzyEquals(posX, 0) || Util.fuzzyEquals(posX, width)) {
			entity.setVelocityX(-entity.getVelocityX()); // Negate the
															// x-velocity.
		}
		else if (Util.fuzzyEquals(posY, 0) || Util.fuzzyEquals(posY, height)) {
			entity.setVelocityY(-entity.getVelocityY()); // Negate the
															// y-velocity.
		}
	}

	/**
	 * Resolves the collision between two entities.
	 * 
	 * @param entity1
	 * @param entity2
	 * @param x
	 * @param y
	 * 
	 * @note for sake of readability, entity1 and entity2 are renamed after
	 *       initial if-test
	 *       to names that indicate which entity is which type.
	 * 
	 * @effect ...
	 *         |if ((entity1 instanceof Ship && entity2 instanceof Ship)||
	 *         (entity1 instanceof Asteroid && entity2 instanceof Asteroid))
	 *         removeBullet(entity1)
	 *         removeBullet(entity2)
	 * @effect ...
	 *         |if ((entity1 instanceof Ship && entity2 instanceof Ship) ||
	 *         (entity1 instanceof Asteroid && entity2 instanceof Asteroid))
	 *         entity1.setVelocity(newVelocity according to formulas in
	 *         objectBounce)
	 *         entity2.setVelocity(newVelocity according to formulas in
	 *         objectBounce)
	 * @effect ...
	 *         |if ((entity1 instanceof Asteroid && entity2 instanceof Bullet)
	 *         || (entity1 instanceof Bullet && entity2 instanceof Asteroid))
	 *         removeBullet(bulletEntity)
	 *         if (asteroidEntity.getRadius() > 30)
	 *         asteroids.add(childAsteroid with properties explained in
	 *         asteroidDeath)
	 *         asteroids.add(childAsteroid with properties explained in
	 *         asteroidDeath)
	 *         removeAsteroid(asteroidEntity)
	 * 
	 * @effect ...
	 *         |if ((entity1 instanceof Ship && entity2 instanceof Bullet) ||
	 *         (entity1 instanceof Bullet && entity2 instanceof Ship))
	 *         if(bulletEntity.getSource() == shipEntity)
	 *         return
	 *         else
	 *         removeBullet(bulletEntity)
	 *         removeShip(shipEntity)
	 * 
	 * @effect ...
	 *         |if ((entity1 instanceof Asteroid && entity2 instanceof Ship) ||
	 *         (entity1 instanceof Ship && entity2 instanceof Asteroid))
	 *         removeShip(shipEntity)
	 * 
	 */
	private void resolveObjectCollision(SpaceObject entity1, SpaceObject entity2) {// Collision position
																					// necessary?
		// Several cases, some can be condensed.

		// Case1: Two ships or two asteroids.
		if ((entity1 instanceof Ship && entity2 instanceof Ship) || (entity1 instanceof Asteroid && entity2 instanceof Asteroid)) {
			objectBounce(entity1, entity2);
		}

		// Case2: Bullet hits other bullet. Both entities die.
		else if (entity1 instanceof Bullet && entity2 instanceof Bullet) {
			colBulletBullet(entity1, entity2);
		}

		// Case3: Bullet hits asteroid. Asteroid spawns 2 smaller asteroids if
		// the radius >= 30km.
		// This is further separated to discern which one is the bullet and
		// which one the asteroid.

		else if ((entity1 instanceof Asteroid && entity2 instanceof Bullet) || (entity1 instanceof Bullet && entity2 instanceof Asteroid)) {
			colBulletAsteroid(entity1, entity2);
		}

		// Case4: Bullet collides with ship
		// Check if the ship is the source of the bullet, if so, just bounce,
		// otherwise both die.
		// Again separated for discern which is which, needs fixing.
		else if ((entity1 instanceof Ship && entity2 instanceof Bullet) || (entity1 instanceof Bullet && entity2 instanceof Ship)) {
			colBulletShip(entity1, entity2);
		}

		// Case5: Asteroid collides with ship. Ship dies, asteroid unaffected.
		// Again, needs separating.
		else if ((entity1 instanceof Asteroid && entity2 instanceof Ship) || (entity1 instanceof Ship && entity2 instanceof Asteroid)) {
			colAsteroidShip(entity1, entity2);
		}
	}

	/**
	 * Calculates and sets the new velocities of 2 objects colliding and
	 * bouncing off each other.
	 * 
	 * @param entity1
	 * @param entity2
	 * 
	 * @effect Bounces 2 objects off each other according to the laws of physics
	 *         |entity1.setVelocityX(entity1.getVelocityX() +
	 *         Jx/entity1.getMass())
	 *         entity1.setVelocityY(entity1.getVelocityY() +
	 *         Jy/entity1.getMass())
	 * 
	 *         entity2.setVelocityX(entity2.getVelocityX() -
	 *         Jx/entity2.getMass())
	 *         entity2.setVelocityY(entity2.getVelocityY() -
	 *         Jy/entity2.getMass())
	 * 
	 *         with Jx == J * (entity2.getPositionX() - entity1.getPositionX())/
	 *         (entity1.getRadius() + entity2.getRadius())
	 *         Jy == J * (entity2.getPositionY() - entity1.getPositionY())/
	 *         (entity1.getRadius() + entity2.getRadius())
	 */
	private void objectBounce(SpaceObject entity1, SpaceObject entity2) {

		//All necessary data for the first object
		double posX1 = entity1.getPositionX();
		double posY1 = entity1.getPositionY();
		double velX1 = entity1.getVelocityX();
		double velY1 = entity1.getVelocityY();
		double mass1 = entity1.getMass();
		double radius1 = entity1.getRadius();

		//All necessary data for the second object
		double posX2 = entity2.getPositionX();
		double posY2 = entity2.getPositionY();
		double velX2 = entity2.getVelocityX();
		double velY2 = entity2.getVelocityY();
		double mass2 = entity2.getMass();
		double radius2 = entity2.getRadius();

		double deltaX = posX2 - posX1;
		double deltaY = posY2 - posY1;

		Vector deltaV = new Vector(velX2 - velX1, velY2 - velY1);
		Vector deltaR = new Vector(deltaX, deltaY);

		double dVdR = Vector.dotProduct(deltaV, deltaR);

		double sigma = radius1 + radius2;

		double J = 2 * (mass1 * mass2 * dVdR) / (sigma * (mass1 + mass2));

		double Jx = J * deltaX / sigma;
		double Jy = J * deltaY / sigma;

		double newVelX1 = velX1 + Jx / mass1;
		double newVelY1 = velY1 + Jy / mass1;

		entity1.setVelocityX(newVelX1);
		entity1.setVelocityY(newVelY1);

		double newVelX2 = velX2 - Jx / mass2;
		double newVelY2 = velY2 - Jy / mass2;

		entity2.setVelocityX(newVelX2);
		entity2.setVelocityY(newVelY2);
	}

	/**
	 * 
	 * @param entity1
	 * @param entity2
	 * 
	 * @effect ...
	 *         |removeBullet(entity1)
	 *         removeBullet(entity2)
	 */
	private void colBulletBullet(SpaceObject entity1, SpaceObject entity2) {
		die(entity1);
		die(entity2);
	}

	/**
	 * 
	 * @param entity1
	 * @param entity2
	 * 
	 * @note entity1 and entity2 are renamed to bulletEntity and asteroidEntity
	 *       to signify which is which type
	 * 
	 * @effect ...
	 *         |removeBullet(bulletEntity)
	 *         if (asteroidEntity.getRadius() > 30)
	 *         asteroids.add(childAsteroid with properties explained in
	 *         asteroidDeath)
	 *         asteroids.add(childAsteroid with properties explained in
	 *         asteroidDeath)
	 *         removeAsteroid(asteroidEntity)
	 * 
	 */
	private void colBulletAsteroid(SpaceObject entity1, SpaceObject entity2) {
		// Case 3.1

		if (entity1 instanceof Asteroid) {

			die(entity2);
			asteroidDeath(entity1);
		}
		// Case 3.2
		else if (entity2 instanceof Asteroid) {

			die(entity1);
			asteroidDeath(entity2);
		}
	}

	/**
	 * Spawns 2 children of an asteroid upon death if the radius of the parent
	 * is larger than 30km.
	 * 
	 * @param asteroid
	 * 
	 * @effect ...
	 *         |if (asteroid.getRadius() > 30)
	 *         radian = random radian
	 *         child1.getRadius() == asteroid.getRadius / 2;
	 *         child1.getTotalVelocity() == asteroid.getTotalVelocity * 1.5
	 *         child1.getDirection() == radian
	 * 
	 *         child2..getRadius() == asteroid.getRadius / 2;
	 *         child2.getTotalVelocity() == asteroid.getTotalVelocity * 1.5
	 *         child1.getDirection() == radian
	 * 
	 *         removeAsteroid(asteroid)
	 *         addAsteroid(child1)
	 *         addAsteroid(child2)
	 *         else
	 *         removeAsteroid(asteroid)
	 */
	private void asteroidDeath(SpaceObject asteroid) {

		if (asteroid.getRadius() > 30) {
			// Selection of a random radian.
			Random random = new Random();

			double degree = random.nextDouble();
			degree = degree * 360;
			double radian = Math.toRadians(degree);

			// Calculation of various aspects of the children.
			double childRadius = asteroid.getRadius() / 2;
			double childTotalVelocity = asteroid.getTotalVelocity() * 1.5;
			Vector childPos1 = new Vector(asteroid.getPositionX() + Math.cos(radian) * childRadius, asteroid.getPositionY() + Math.sin(radian) * childRadius);
			Vector childPos2 = new Vector(asteroid.getPositionX() - Math.cos(radian) * childRadius, asteroid.getPositionY() - Math.sin(radian) * childRadius);
			Vector childVel1 = new Vector(Math.cos(radian) * childTotalVelocity, Math.sin(radian) * childTotalVelocity);
			Vector childVel2 = new Vector(-Math.cos(radian) * childTotalVelocity, -Math.sin(radian) * childTotalVelocity);

			// Kill parent
			die(asteroid);

			// Spawning may give some problems regarding overlap.
			Asteroid child1 = new Asteroid(childRadius, childVel1, childPos1, this);
			Asteroid child2 = new Asteroid(childRadius, childVel2, childPos2, this);
			addAsteroid(child1);
			addAsteroid(child2);
		}
		else die(asteroid);
	}

	/**
	 * 
	 * @param entity1
	 * @param entity2
	 * 
	 * @note entity1 and entity2 are renamed to bulletEntity and shipEntity to
	 *       signify which is which type
	 * 
	 * 
	 * @effect ...
	 *         |if ((entity1 instanceof Ship && entity2 instanceof Bullet) ||
	 *         (entity1 instanceof Bullet && entity2 instanceof Ship))
	 *         if(bulletEntity.getSource() == shipEntity)
	 *         return
	 *         else
	 *         removeBullet(bulletEntity)
	 *         removeShip(shipEntity)
	 */
	private void colBulletShip(SpaceObject entity1, SpaceObject entity2) {
		// Case4.1
		if (entity1 instanceof Ship) {
			if (((Bullet) entity2).getSource() == entity1) {
				return;
			}
			else {
				die(entity1);
				die(entity2);
			}
		}
		// Case4.2
		else if (entity2 instanceof Ship) {
			if (((Bullet) entity1).getSource() == entity2) {
				return;
			}
			else {
				die(entity1);
				die(entity2);
			}
		}
	}

	/**
	 * 
	 * @param entity1
	 * @param entity2
	 * 
	 * @effect ...
	 *         |if ((entity1 instanceof Asteroid && entity2 instanceof Ship) ||
	 *         (entity1 instanceof Ship && entity2 instanceof Asteroid))
	 *         removeShip(shipEntity)
	 */
	private void colAsteroidShip(SpaceObject entity1, SpaceObject entity2) {
		// Case5.1
		if (entity1 instanceof Asteroid) {
			die(entity2);
		}
		// Case5.2
		else if (entity2 instanceof Asteroid) {
			die(entity1);
		}
	}

	/**
	 * Destructs the given entity and removes it from the lists.
	 * 
	 * @param entity
	 * 
	 * @effect ...
	 *         |if(entity instanceof Ship)
	 *         removeShip(entity)
	 * 
	 * @effect ...
	 *         |if(entity instanceof Asteroid)
	 *         removeAsteroid(entity)
	 * 
	 * @effect ...
	 *         |if(entity instanceof Bullet)
	 *         removeBullet(entity)
	 */
	private void die(SpaceObject entity) {
		// Removes objects from the world.
		if (entity instanceof Ship) {
			removeShip((Ship) entity);
		}
		else if (entity instanceof Asteroid) {
			removeAsteroid((Asteroid) entity);
		}
		else if (entity instanceof Bullet) {
			removeBullet((Bullet) entity);
		}
	}

}
